package com.sinodata.bsm.cicp.util.telnet;

import java.io.IOException;

import org.apache.log4j.Logger;

import de.mud.telnet.TelnetWrapper;

/**
 * 利用 JTA 实现的telnet.
 * 连接“联机指令平台”的Virtual Port Service使用
 *
 * @author thomas.h.zhang
 */
public class VirtualPortServiceTelnet extends TelnetWrapper implements Telnet {
    private final static Logger logger = Logger.getLogger(VirtualPortServiceTelnet.class);
    /**
     * 超时时长.
     */
    private final int timeoutsecond = 30; // 10's

    public VirtualPortServiceTelnet() {
    }

    public void login(String user, String pwd, String prompt) throws TelnetException {
        try {
            login(user, pwd);
            setPrompt(prompt);
        } catch (IOException e) {
            logger.error("Login on " + host + ":" + port + " " + user + "/" + prompt + " exception!", e);
            throw new TelnetException("Login on " + host + ":" + port + " " + user + "/" + prompt + " exception!", e);
        }

        // 接收登录提示
        // Last login: Tue Jul 3 23:52:42 from 192.168.9.29
        // Sun Microsystems Inc. SunOS 5.10 Generic January 2005
        // this.runTask(null);
    }

    /**
     * Send a command to the remote host. A newline is appended and if a prompt
     * is set it will return the resulting data until the prompt is encountered.
     *
     * @param cmd
     *            the command
     * @return output of the command or null if no prompt is set
     */
    public void sendOnly(String cmd) throws IOException {
        byte arr[];
        arr = (cmd + "\n").getBytes();
        handler.transpose(arr);
    }

    /**
     * 发送shell指令到目的主机.
     */
    public void sendCmd(String cmd) throws IOException {
        sendOnly(cmd);
    }

    /**
     * 接受shell执行结果.
     */
    public String recvResp() throws IOException {
        String rs = "";
        while (true) {
            int n = in.read(buf);
            if (n < 0) {
                logger.error("Failed to recvice data from telnet server " + host);
                throw new IOException("Failed to recvice data from telnet server " + host);
            }

            String s = new String(buf, 0, n);
            System.err.print(s);
            rs += s;
            // if (s.indexOf(this.getPrompt()) >= 0)
            if (isTermianl(rs)) {
                return rs;
            }
        }
        //        if (getPrompt() != null)
        //            return waitfor(getPrompt());
        //        return null;
    }

    /**
     * 这是一个经验值, Shell 执行结果的每行长度不会大于256个字符. 但又不能分配得太小, 经实际测试 1024 为一个比较理想的尺寸.
     */
    private final byte[] buf = new byte[1024];

    // 判断结尾处是否为提示符.
    // 结束符必须在末尾.
    private boolean isTermianl(String buf) {
        int promptLen = getPrompt().length();
        int bufLen = buf.length();
        if (promptLen > bufLen) {
            return false;
        }

        String last = buf.substring(bufLen - promptLen, bufLen);
        if (getPrompt().equals(last)) {
            return true;
        } else {
            // 末尾包含一个空格的提示符
            String spacePrompt = getPrompt() + " ";
            promptLen = spacePrompt.length();
            if (promptLen > bufLen) {
                return false;
            }
            last = buf.substring(bufLen - promptLen, bufLen);
            if (spacePrompt.equals(last)) {
                return true;
            }
        }

        return false;
    }

    /**
     * 发送取消执行shell的指令, 及发送一个^C
     */
    public void cancel() throws IOException {
        sendCmd("\003"); // ^C
        recvResp();
    }

    public void runLogin(String user, String pwd, String prompt) throws TelnetException {
        LoginTask task = new LoginTask(this, user, pwd, prompt);
        task.start();

        int count = 0;
        // wait until last task is complete
        while (!task.isComplete() || task.isActive()) {
            try {
                Thread.sleep(1000);
                count++;
                if (count >= timeoutsecond) {
                    Thread.interrupted();
                    task.stop();
                    logger.error("Login timeout: " + timeoutsecond + "s");
                    throw new TelnetTaskTimeoutException("Login timeout: " + timeoutsecond + "s");
                }
            } catch (InterruptedException e) {
            }
        }
    }

    public String runTask(String shell) throws TelnetException {
        TelnetTask task = new TelnetTask(this, shell);
        task.start();

        int count = 0;
        // wait until last task is complete
        while (!task.isComplete() || task.isActive()) {
            try {
                Thread.sleep(1000);
                count++;
                if (count >= timeoutsecond) {
                    try {
                        cancel();
                    } catch (IOException e) {
                    }
                    Thread.interrupted();
                    task.stop();
                    logger.error("Execute the command:[" + task.getCmd() + "] timeout: " + timeoutsecond + "s");
                    throw new TelnetTaskTimeoutException("Execute the command:[" + task.getCmd() + "] timeout: " + timeoutsecond + "s");
                }
            } catch (InterruptedException e) {
                logger.error(e);
            }
        }

        return task.getShellOut();
    }

    public static void main(String[] args) throws Exception {
        VirtualPortServiceTelnet telnet = new VirtualPortServiceTelnet();

        String host = "192.168.5.63";
        int port = 14556;
        //        String user = "administrator";
        //        String pwd = "ms";
        //        String prompt = ">";
        //        String cmd = "ls -l";
        String recv;

        telnet.connect(host, port);
        //telnet.login(user, pwd, prompt);
        telnet.sendCmd("administrator");
        telnet.setPrompt("login:");
        recv = telnet.recvResp();
        System.out.println("shell out1: [" + recv + "]");

        telnet.setPrompt("password:");
        telnet.sendCmd("ms");
        recv = telnet.recvResp();
        System.out.println("shell out2: [" + recv + "]");

        telnet.setPrompt("password:");
        recv = telnet.recvResp();
        System.out.println("shell out3: [" + recv + "]");

        telnet.setPrompt("C:\\Documents and Settings\\Administrator>");
        recv = telnet.recvResp();
        System.out.println("shell out4: [" + recv + "]");

        telnet.sendCmd("dir");
        //telnet.setPrompt("C:\\Documents and Settings\\Administrator>");
        recv = telnet.recvResp();
        System.out.println("shell out: [" + recv + "]");

        telnet.sendCmd("dir");
        recv = telnet.recvResp();
        System.out.println("shell out: [" + recv + "]");

        telnet.sendCmd("df -k");
        recv = telnet.recvResp();
        System.out.println("shell out: [" + recv + "]");

        telnet.disconnect();
    }

    private boolean isValid = true;

    public boolean getIsValid() {
        return this.isValid;
    }

    public void setIsValid(boolean isValid) {
        this.isValid = isValid;

    }

}
